package com.zhkc.wisdom.commom.mybatis.mate.handler;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import com.zhkc.wisdom.commom.mybatis.mate.annotation.DataPermissions;
import com.zhkc.wisdom.commom.mybatis.mate.annotation.DataScope;
import com.zhkc.wisdom.commom.mybatis.mate.config.DataColumnType;
import com.zhkc.wisdom.commom.mybatis.mate.annotation.DataColumn;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 数据权限拼装逻辑处理
 */
public class DataScopeHandler implements MultiDataPermissionHandler {

    /**
     * 获取数据权限 SQL 片段。
     * <p>旧的 {@link MultiDataPermissionHandler#getSqlSegment(Expression, String)} 方法第一个参数包含所有的 where 条件信息，如果 return 了 null 会覆盖原有的 where 数据，</p>
     * <p>新版的 {@link MultiDataPermissionHandler#getSqlSegment(Table, Expression, String)} 方法不能覆盖原有的 where 数据，如果 return 了 null 则表示不追加任何 where 条件</p>
     *
     * @param table             所执行的数据库表信息，可以通过此参数获取表名和表别名
     * @param where             原有的 where 条件信息
     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法
     * @return JSqlParser 条件表达式，返回的条件表达式会拼接在原有的表达式后面（不会覆盖原有的表达式）
     */
    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
        try {
            /**
             * 数据权限逻辑
             *
             * 1、判断该类是否需要进行数据权限处理（权限注解，标识需要解析）
             * 2、权限注解已方法上的注解优先，获取所有方法
             * 3、判断方法名 = methodName的方法上是否存在权限注解
             * 4、如果存在权限注解，解析该注解执行逻辑
             * 5、如果不存在权限注解，获取类上上的权限注解，如果没有不处理，如果有执行该注解逻辑
             *
             * 用户的点位权限目前设计：存放在当前的线程里面，同获取用户id一样，需要修改token的设计，将jwt的token放入redis，
             * 生成uuid作为前后端传递的token，这样token会比较小。
             *
             */
            //类名
            Class<?> mapperClazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(".")));
            //当前执行sql的方法名
            String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);

            /**
             * DataScope注解优先级：【方法上 > 类上】
             */
            DataPermissions checkPermission = mapperClazz.getAnnotation(DataPermissions.class);
            if (ObjectUtils.isEmpty(checkPermission)) {
                return null;
            }
            InExpression jcdIdInExpression = null;
            boolean methodDataScope = false;
            for (Method method : mapperClazz.getDeclaredMethods()) {
                if (method.getName().equals(methodName) || (method.getName() + "_COUNT").equals(methodName) || (method.getName() + "_count").equals(methodName)) {
                    DataScope dataScope = method.getAnnotation(DataScope.class);
                    if (ObjectUtils.isNotEmpty(dataScope)) {
                        methodDataScope = true;
                        jcdIdInExpression = addExpression(dataScope, jcdIdInExpression);
                    }
                }
            }

            if (!methodDataScope) {
                DataScope dataScope = mapperClazz.getAnnotation(DataScope.class);
                jcdIdInExpression = addExpression(dataScope, null);
            }
            return jcdIdInExpression;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }


    private InExpression addExpression(DataScope dataScope, InExpression jcdIdInExpression) {
        if (ObjectUtils.isNotEmpty(dataScope)) {
            DataColumn[] columns = dataScope.value();
            if (columns == null) {
                return jcdIdInExpression;
            }
            for (int i = 0; i < columns.length; i++) {
                if (DataColumnType.JCD_ID.equals(columns[i].name())) {
                    //监测点追加in条件
                    jcdIdInExpression = additionalClauses(columns[i], jcdIdInExpression);
                }
            }
        }
        return jcdIdInExpression;
    }

    private InExpression additionalClauses(DataColumn dataColumn, InExpression jcdIdInExpression) {

        List<Long> jcdList = new ArrayList<>();
        jcdList.add(4L);
        jcdList.add(5L);

        String alias = dataColumn.alias();
        String fieldName = dataColumn.name();

        /**
         * 构造监测点in表达式。
         */
        if (CollectionUtils.isNotEmpty(jcdList)) {
            jcdIdInExpression = new InExpression();
            ExpressionList jcdIds = new ExpressionList(jcdList);
            // 设置左边的字段表达式，右边设置值。
            jcdIdInExpression.setLeftExpression(buildColumn(alias, fieldName));
            jcdIdInExpression.setRightExpression(new Parenthesis(jcdIds));
        }
        return jcdIdInExpression;
    }


    /**
     * 构建Column
     *
     * @param tableAlias 表别名
     * @param columnName 字段名称
     * @return 带表别名字段
     */
    public static Column buildColumn(String tableAlias, String columnName) {
        if (StringUtils.isNotEmpty(tableAlias)) {
            columnName = tableAlias + "." + columnName;
        }
        return new Column(columnName);
    }

}

